import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


/**
 * 什么叫虚假唤醒？
 *   站在上述两个消费者线程的角度上讲, 无论哪一个线程抢到了资源, 另一个线程的唤醒就可以被认为是没有必要的, 也就是被虚假唤醒了。
 *
 * 虚假唤醒发生的场景以及解决方式？
 *   上述代码使用if判断，唤醒后线程会从await之后的代码开始运行，但是不会重新判断if条件，直接继续运行if代码块之后的代码，而如果使用while的话，
 *    也会从await之后的代码运行，但是唤醒后会重新判断循环条件，如果不成立再执行while代码块之后的代码块，成立的话继续await。
 *
 */
class ShareData  //资源类
{
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void increment() throws Exception{
        lock.lock();
        try{
            while(number != 0) //不能用if,防止终端和虚假唤醒，出现多加多减的情况
            {
                //等待不能生产
                condition.await();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            //唤醒
            condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }

    }

    public void decrement() throws Exception{
        lock.lock();
        try{
            while(number == 0)
            {
                //等待不能消费
                condition.await();
            }
            //干活
            number--;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            //唤醒
            condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }

    }
}

/**
 * 传统版生产者消费者
 * 一个初始值为零的变量，两个线程对其交替操作，一个加1一个减1，进行五轮
 *
 *
 * syc wait  notify    |   lock  await  signal
 */
public class ProdConsumer_TraditionDemo
{
    public static void main(String[] args) {
        ShareData  shareDate = new ShareData();

        new Thread(() -> {
            try{
                for(int i = 0;i <= 5;i++){
                    try{
                        shareDate.increment();
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }

            }catch (Exception e){
                e.printStackTrace();
            }
        },"AAA").start();

        new Thread(() -> {
            try{
                for(int i = 0;i <= 5;i++){
                    try{
                        shareDate.decrement();
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }

            }catch (Exception e){
                e.printStackTrace();
            }
        },"BBB").start();
    }
}
